package com.spzx.common.rabbit.config;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.spzx.common.rabbit.entity.GuiguCorrelationData;
import com.spzx.common.rabbit.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/**
 * @Author ChenXing
 * @Description 监听类,当消息投递失败或是发送失败，会来到这里，才好进行下一步操作
 * @Create 2024/8/16 19:58
 */

@Slf4j
@Component
public class RabbitInitConfigApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        this.setupCallbacks();
    }

    private void setupCallbacks() {


        // 确认是否发送成功  发送到交换机 (无论成功或者失败，都会来这)
        // correlationData：是与发送消息相关的元数据，可以是消息的唯一标识符或其他自定义数据。
        // ack：是一个布尔值，表示消息是否成功到达交换机。如果为true，则消息已成功到达；如果为false，则消息发送失败。
        // reason：如果ack为false，reason将提供失败的原因。
        this.rabbitTemplate.setConfirmCallback((correlationData, ack, reason) -> {
            if (ack) {
                //消息到交换器成功
                log.info("消息发送到Exchange成功：{}", correlationData);
            } else {
                //消息到交换器失败 要有消息的补偿策略(重试)
                log.error("消息发送到Exchange失败：{}", reason);

                // 拿到原始消息
                GuiguCorrelationData guiguCorrelationData = (GuiguCorrelationData)correlationData;
                // 重新发送，计算次数
                // 这里还是得从缓存里面拿，不然 retryCount 数量一直下不来
                String id = guiguCorrelationData.getId();

                retrySendMsg(guiguCorrelationData);
            }
        });


        // 确认是否投递成功  交换机投递到队列 （只有失败才来这）
        this.rabbitTemplate.setReturnsCallback(returned ->{
          // 投递失败后，会将 correlationData 主键id返回给回调方法
            String id = returned.getMessage().getMessageProperties().getHeader("spring_returned_message_correlation");
              String guiguCorrelationDataCachestr = (String)redisTemplate.opsForValue().get(id);
              GuiguCorrelationData guiguCorrelationDataCache = JSON.parseObject(guiguCorrelationDataCachestr, GuiguCorrelationData.class);
            retrySendMsg(guiguCorrelationDataCache);
            log.info("消息投递失败结果：{}", returned);
            System.out.println("投递失败");


        });

    }

    private void retrySendMsg(GuiguCorrelationData guiguCorrelationData) {
        // 消息重发
        int retryCount = guiguCorrelationData.getRetryCount();
        boolean delay = guiguCorrelationData.isDelay();
        String exchange = guiguCorrelationData.getExchange();
        String routingKey = guiguCorrelationData.getRoutingKey();
        Object message = guiguCorrelationData.getMessage();
        String id = guiguCorrelationData.getId();
        if(delay){
            // 判断是不是延迟队列，如果是的话，就直接结束，不用重发
            return;
        }
        if(retryCount>0){
            // 重新发送消息
            retryCount--;
           // rabbitService.sendMessage(exchange,routingKey,message);
            rabbitTemplate.convertAndSend(exchange, routingKey, message,guiguCorrelationData);
            guiguCorrelationData.setRetryCount(retryCount);
            // 重新覆盖缓存中的原始信息
            redisTemplate.opsForValue().set(id, JSON.toJSONString(guiguCorrelationData));
        }else {
            System.out.println("重试结束");
        }

    }
}
